package com.lock.optimisticLock.service;

import com.lock.optimisticLock.model.Account;
import com.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.Transaction;

import java.util.ArrayList;

public class BusinessService extends Thread {
    public static SessionFactory sessionFactory = new HibernateUtil().getSessionFactory();

    private String transactionType;
    private Log log;

    public BusinessService(String transactionType, Log log) {
        this.transactionType = transactionType;
        this.log = log;
    }
    public void run() {
        try {
            if (transactionType.equals("withdraw"))
                withdraw();
            else
                transferCheck();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void withdraw() throws Exception {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {

            tx = session.beginTransaction();
            log.write("withdraw():开始事务");
            Thread.sleep(500);

            Account account = (Account) session.get(Account.class, 1L);

            log.write("withdraw():查询到存款余额为：balance=" + account.getBalance());
            Thread.sleep(500);

            account.setBalance(account.getBalance() - 100);
            log.write("withdraw():取出100元，把存款余额改为：" + account.getBalance());

            tx.commit();
            log.write("withdraw():提交事务");
            Thread.sleep(500);

        } catch (StaleObjectStateException e) {
            if (tx != null) {
                tx.rollback();
            }
            System.out.println("账户信息已被其他事务修改,本事务被撤销，请重新开始取款事务");
            log.write("withdraw():账户信息已被其他事务修改,本事务被撤销");
        } catch (RuntimeException e) {
            if (tx != null) {
                tx.rollback();
            }
            throw e;
        } finally {
            session.close();
        }
    }

    public void transferCheck() throws Exception {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {

            tx = session.beginTransaction();
            log.write("transferCheck():开始事务");
            Thread.sleep(500);

            Account account = (Account) session.get(Account.class, 1L);

            log.write("transferCheck():查询到存款余额为：balance=" + account.getBalance());
            Thread.sleep(500);

            account.setBalance(account.getBalance() + 100);
            log.write("transferCheck():汇入100元，把存款余额改为：" + account.getBalance());

            tx.commit();
            log.write("transferCheck():提交事务");
            Thread.sleep(500);

        } catch (StaleObjectStateException e) {
            if (tx != null) {
                tx.rollback();
            }
            System.out.println("账户信息已被其他事务修改,本事务被撤销，请重新开始支票转账事务");
            log.write("transferCheck():账户信息已被其他事务修改,本事务被撤销");
        } catch (RuntimeException e) {
            if (tx != null) {
                tx.rollback();
            }
            throw e;
        } finally {
            session.close();
        }
    }

    private void registerAccount() {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            tx = session.beginTransaction();
            System.out.println("插入数据");
            Account account = new Account();
            account.setName("Tom");
            account.setBalance(1000);
            session.save(account);
            tx.commit();

        } catch (RuntimeException e) {
            System.out.println("");
            if (tx != null) {
                tx.rollback();
            }
            throw e;
        } finally {
            session.close();
        }
    }

    public static void main(String args[]) throws Exception {
        Log log = new Log();
        Thread withdrawThread = new BusinessService("withdraw", log);
        Thread transferCheckThread = new BusinessService("transferCheck", log);

        ((BusinessService) withdrawThread).registerAccount();

        withdrawThread.start();
        transferCheckThread.start();

        while (withdrawThread.isAlive() || transferCheckThread.isAlive()) {
            Thread.sleep(100);
        }
        log.print();
        sessionFactory.close();
    }
}

class Log {
    private ArrayList logs = new ArrayList();

    synchronized void write(String text) {
        logs.add(text);
    }
    void print() {
        for (Object log : logs) {
            System.out.println(log);
        }
    }
}
